package com.example.mall.product.service.impl;

import com.alibaba.fastjson2.JSON;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.mall.common.utils.PageUtils;
import com.example.mall.common.utils.Query;

import com.example.mall.product.dao.CategoryDao;
import com.example.mall.product.entity.CategoryEntity;
import com.example.mall.product.service.CategoryService;


@Service("categoryService")
public class CategoryServiceImpl extends ServiceImpl<CategoryDao, CategoryEntity> implements CategoryService {

    private static final String CATEGORY_KEY = "com.example.mall.product.service.impl.CategoryServiceImpl.listCategoryTree";
    private static final String CATEGORY_SERVICE_IMPL_LOCK = "com.example.mall.product.service.impl.CategoryServiceImpl.lock";

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private RedissonClient redissonClient;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<CategoryEntity> page = this.page(
                new Query<CategoryEntity>().getPage(params),
                new QueryWrapper<CategoryEntity>()
        );

        return new PageUtils(page);
    }

    /**
     * 查询三级分类树
     * @return 返回三级分类树
     */
    @Cacheable(value = {"category"}, key = "#root.methodName")
    @Override
    public List<CategoryEntity> listCategoryTree() {
        log.debug("调用了方法 --------- ---------");
        List<CategoryEntity> categoryEntityList = baseMapper.selectList(null);
        log.debug("查询了数据库 --------- ---------");
        Map<Long, List<CategoryEntity>> categoryEntityMap = categoryEntityList.stream().collect(Collectors.groupingBy(CategoryEntity::getParentCid));
        categoryEntityList.forEach(categoryEntity -> categoryEntity.setCategoryEntityList(categoryEntityMap.getOrDefault(categoryEntity.getCatId(), new ArrayList<>(1))));
        return categoryEntityList.stream().filter(f -> f.getParentCid() == 0L).sorted(Comparator.comparingInt(CategoryEntity::getSort)).collect(Collectors.toList());
    }

//    @Override
//    public List<CategoryEntity> listCategoryTree() {
//        String categoriesFromRedis = stringRedisTemplate.opsForValue().get(CATEGORY_KEY);
//        return StringUtils.isEmpty(categoriesFromRedis)
//                ? getCategoryEntitiesForRedisLock()
//                : JSON.parseArray(categoriesFromRedis, CategoryEntity.class);
//    }

    @NotNull
    private List<CategoryEntity> getCategoryEntitiesForRedisLock() {
//        return getCategoryEntityListForOldRedisLock();
//        return getCategoryEntityListForRedissonLock();
        return getCategoryEntityTree();
    }

    /**
     * Redisson分布式锁
     * @return List<CategoryEntity>
     */
    private List<CategoryEntity> getCategoryEntityListForRedissonLock() {
        ValueOperations<String, String> stringValueOperations = stringRedisTemplate.opsForValue();
        RLock lock = redissonClient.getLock(CATEGORY_SERVICE_IMPL_LOCK);
        lock.lock(15, TimeUnit.SECONDS);
        try {
            String categoriesFromRedis = stringValueOperations.get(CATEGORY_KEY);
            if (!StringUtils.isEmpty(categoriesFromRedis)) {
                return JSON.parseArray(categoriesFromRedis, CategoryEntity.class);
            }
            List<CategoryEntity> categoryEntities = getCategoryEntityTree();
            stringValueOperations.set(CATEGORY_KEY, JSON.toJSONString(categoryEntities), 12 * 3600, TimeUnit.SECONDS);
            return categoryEntities;
        } finally {
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    /**
     * redis分布式锁
     * @return List<CategoryEntity>
     */
    @Deprecated
    private List<CategoryEntity> getCategoryEntityListForOldRedisLock() {
        // UUID 确保每个请求处理完成后释放的是自己的锁
        String lockValue = UUID.randomUUID().toString();

        ValueOperations<String, String> stringValueOperations = stringRedisTemplate.opsForValue();
        // 加锁
        Boolean isAcquiredLock = stringValueOperations.setIfAbsent(CATEGORY_SERVICE_IMPL_LOCK, lockValue, 5, TimeUnit.SECONDS);

        if (Boolean.TRUE.equals(isAcquiredLock)) {
            String categoriesFromRedis = stringValueOperations.get(CATEGORY_KEY);
            if (!StringUtils.isEmpty(categoriesFromRedis)) {
                releaseLock(lockValue);
                return JSON.parseArray(categoriesFromRedis, CategoryEntity.class);
            }
            List<CategoryEntity> categoryEntities = getCategoryEntityTree();
            stringValueOperations.set(CATEGORY_KEY, JSON.toJSONString(categoryEntities), 12 * 3600, TimeUnit.SECONDS);

            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            // 释放锁
            releaseLock(lockValue);
            return categoryEntities;
        }

        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return getCategoryEntitiesForRedisLock();
    }

    private void releaseLock(String lockValue) {
        // 释放锁
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        stringRedisTemplate.execute(
                new DefaultRedisScript<>(script, Long.class),
                Collections.singletonList(CATEGORY_SERVICE_IMPL_LOCK),
                lockValue);
    }

    @NotNull
    public List<CategoryEntity> getCategoryEntityTree() {
        return listCategoryTree();
    }

    @Override
    public void removeMenusByIds(List<Long> catIds) {
        baseMapper.deleteBatchIds(catIds);
    }

    /**
     * 修改商品三级分类
     * * @CacheEvict 失效模式 删除缓存中数据  allEntries = true：删除所有分区为"category"的缓存
     * * @Caching 可以指定删除多个分区中缓存
     * * @CachePut 对应双写模式 修改缓存中数据
     * @param category 商品三级分类实例
     * @return boolean
     */
//    @CacheEvict(value = {"category"}, key = "'listCategoryTree'", allEntries = true)
//    @Caching(evict = {
//            @CacheEvict(value = "category", key = "'listCategoryTree'"),
//            ...
//    })
//    @CachePut(value = {"category"}, key = "'listCategoryTree'")
    @CacheEvict(value = {"category"}, key = "'listCategoryTree'")
    @Override
    public boolean updateById(CategoryEntity category) {
        return baseMapper.updateById(category) > 0;
    }

}